﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Multithread;
using QP =Multithread.QueueProcessor;
using System.Diagnostics;

namespace MultiThreadConsole
{
    class Program
    {
// ReSharper disable FieldCanBeMadeReadOnly.Local
// ReSharper disable InconsistentNaming
        static AutoResetEvent autoEvent = new AutoResetEvent(false);
// ReSharper restore InconsistentNaming
// ReSharper restore FieldCanBeMadeReadOnly.Local
        private const int MaxThreads = 2000;
        private const int ItemsToQueue = 50000;
        static void Main(string[] args)
        {

            Console.Write("\nWhat do you want to do next?  [g] to continue, [x] to exit ");
            var input = Console.ReadKey();
            while (input.KeyChar != 'x')
            {
                switch (input.KeyChar)
                {
                    case 'g':
                        Console.WriteLine("\rExcelent... now running evil experiment!");
                        RunExperiment();
                        Console.Write("\nWhat do you want to do next?  [g] to continue, [x] to exit: ");
                        break;
                    default:
                        Console.Write(String.Format("\row cumon, of all things you pick '{0}'?!.  [g] to continue, [x] to exit: ", input.KeyChar));
                        break;
                }           
                input = Console.ReadKey();
            }

                 
        }

        private static void RunExperiment()
        {
            QueueItems();
            var stopWatch = new Stopwatch();

            //setup and test ThreadStart
            var qCount1Before = QueueProcessor.QueueCount;
            QueueProcessor.MaxThreadCount = 0;
            stopWatch.Start();
            Console.WriteLine("\nProcessing using ThreadStart");
            ProcessQueueUsingThreadStart();
            stopWatch.Stop();
            var elapseTime1 = stopWatch.ElapsedMilliseconds;
            var qCount1After = QueueProcessor.QueueCount;
            var maxThreadCount1 = QueueProcessor.MaxThreadCount;
            Console.WriteLine("\rA) {0} items left in the Queue after {1:0.00} s", QueueProcessor.QueueCount, elapseTime1 / 1000.0);
            
            //setup and test QueueUserWorkItem
            QueueItems();
            var qCount2Before = QueueProcessor.QueueCount;
            QueueProcessor.MaxThreadCount = 0;
            stopWatch.Restart();
            Console.WriteLine("\nProcessing using QueueUserWorkItem");
            ProcessQueueUsingQueueUserWorkItem();
            while (QueueProcessor.QueueCount > 0)
            {
                autoEvent.WaitOne(); //wait for the queue to finish processing
            }
            stopWatch.Stop();
            var qCount2After = QueueProcessor.QueueCount;
            var maxThreadCount2 = QueueProcessor.MaxThreadCount;
            var elapseTime2 = stopWatch.ElapsedMilliseconds;
            Console.WriteLine("\rB){0} items left in the Queue after {1:0.00} s", QueueProcessor.QueueCount, elapseTime2 / 1000.0);
            
            //setup and test in a single thread!
            QueueItems();
            var qCount3Before = QueueProcessor.QueueCount;
            stopWatch.Restart();
            //process queue using main thread
            Console.WriteLine("\nProcessing using just main thread");
            QueueProcessor.WorkMethod(new ThreadInfo("main thread", autoEvent, 1));
            stopWatch.Stop();
            var qCount3After = QueueProcessor.QueueCount;
            stopWatch.Stop();
            var elapseTime3 = stopWatch.ElapsedMilliseconds;
            Console.WriteLine("\rC) {0} items left in the Queue after {1:0.00} s", QueueProcessor.QueueCount, elapseTime3 / 1000.0);

            Console.WriteLine(string.Format("\nThreadStart Completed in {0:0.00} seconds. MaxThreadsUsed: {1}", elapseTime1 / 1000.0, maxThreadCount1));
            Console.WriteLine(string.Format("QueUserWorkItem Completed in {0:0.00} seconds.  MaxThreadsUsed: {1}", elapseTime2 / 1000.0, maxThreadCount2));
            Console.WriteLine(string.Format("Single Thread Completed in {0:0.00} seconds. ", elapseTime3 / 1000.0));
    
        }

        private static void QueueItems()
        {
            for (int i = 0; i < ItemsToQueue; i++) { QP.Enqueue("hello " + i); }
        }

      

        private static void ProcessQueueUsingThreadStart()
        {
            //with 1 thread, 19.09 seconds
            //with 20 threads, 18.04 seconds
            //with 30 threads, 17.84 seconds
            //with 1000 threads, 28 seconds
            var qs = new List<QueueProcessor>();
            int i = 0;
            while (i <= MaxThreads && QueueProcessor.QueueCount > 0)
            {
                    var queueItem = new QueueProcessor("T" + i);
                    queueItem.BeginProcessData();
                    qs.Add(queueItem);
                    i++;
            }


            //wait for each process to finish
            foreach (QueueProcessor q in qs)
            {
                q.EndProcessData();
            }
        }


        private static void ProcessQueueUsingQueueUserWorkItem()
        {
          
            var qs = new List<QueueProcessor>();
            int i = 1;
            while (i<=MaxThreads && QueueProcessor.QueueCount>0) {
                ThreadPool.QueueUserWorkItem(new WaitCallback(QueueProcessor.WorkMethod), new ThreadInfo("Thread " + i, autoEvent, i));
                i++;
            }
            //wait for each process to finish
            foreach (QueueProcessor q in qs)
            {
                q.EndProcessData();
            }
        }

       
    }
}
